{***************************************************************
    SpyGestion  Copyright (C) 2008 Christophe KOALI <nature2gas@gmail.com>
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
****************************************************************}
unit modCore;

interface

uses modBaseType, Classes, SysUtils, ESBDates, sFonctionsChaines, modBaseRessources;







// Renvoi en seconde, le temps necessaire pour produire les ressources désirées
// il faudra soustraire les ressources déjà existantes pour pouvoir avoir la bonne
// évaluation
function GetTimeNeedToProductThisRes(ResToProduct: TOGRessourceAll; ProductionarHeure: TOGRessourceAll; var ReasonIDResMax: integer): integer;


// Estime les valeurs lorsque l'activité est à moins de 100%
procedure GetRessourceWithBestEstimationOfActivity100(Res: TList);


// Après le parsing, on a besoin de savoir combien on produit sans la contrainte
// du facteur de production, cette fonction s'occupe de calculer ceci
procedure GetRessourceWithoutFactorProduct(Res: TList);

// Ok, après avoir adapté (en effectuant le parsing avec 100% de partout)
// on peut modifier l'activite, cela modifie directement le pointeur,
// il faut donc en faire une sauvegarde avant
procedure SetRessourceActivity(uRes: POGResUsed; Activite: integer);

// Pour simplifier le calcul lors de la fonction de recherche du meilleur
// pourcentage de production, on modifie directement les propriétés Activity
// et on met tout à jour avec cette fonction
procedure UpdateWithTheActivityPropertie(Res: TList);



// Après avoir tout bien réglé, il faut prendre en compte le facteur de production
// pour  connaitre la vrai production, cette fonction s'en occupe et
// renvoi le facteur de production
function GetRessourceWithFactorProduct(Res: TList): double;








// Simple COPIE de pointeur
procedure InjectTList(Source, Destination: TList);

procedure UpdateTList(Source, Destination: TList);
procedure FreeTList(List: TList);

function mpcExtractDureeFromStr(DureeStr: string): integer;
function mpcExtractRessourceFromBuffer(Tampon: string): TOGRessourceAll;
function mpcExtractPlanetLocation(s: string): TPlanetLocation;


implementation

uses modLanguageParsing;


procedure FreeTList(List: TList);
var i: integer;
    CurrRes : POGResUsed;
begin

    if List.Count =  0 then exit;
    
    for i := List.Count - 1 downto 0 do begin
        CurrRes := List.Items[i];
        Dispose(CurrRes);
        List.Delete(i);
    end;

end;

procedure InjectTList(Source, Destination: TList);
var i: integer;
    p: pointer;
begin


    if Source.Count = 0 then exit;

    for i := 0 to Source.Count -1 do begin
        p := Source.Items[i];
        Destination.Add(p);
    end;


end;



procedure UpdateTList(Source, Destination: TList);
var i :integer;
    x, y: POGResUsed;
begin

    if Source.Count = 0 then exit;

    for i := 0 to Source.Count - 1 do begin
       y := Source.Items[i];
       x := Destination.Items[i];

       //x.IdOpt := y.IdOpt;
       x.prMetal := y.prMetal;
       x.prCristal  := y.prCristal;
       x.prDeuterium  := y.prDeuterium;
       x.prEnergie  := y.prEnergie;
       x.csEnergieUsed  := y.csEnergieUsed;
       x.csEnergieAllow  := y.csEnergieAllow;
       //x.Activite        := y.Activite;
    end;

end;






////////////////////////////////
// Renvoi en seconde, le temps necessaire pour produire les ressources
// désirées, ne prend pas en compte l'énergie
// ReasonIDResMax permettra d'équilibré plus rapidement les niveaux de production
function GetTimeNeedToProductThisRes(ResToProduct: TOGRessourceAll; ProductionarHeure: TOGRessourceAll; var ReasonIDResMax: integer): integer;
var Temp: integer;
begin

    Result := 0;

    if ResToProduct.Metal > 0 then begin
       Result := mbrGetTimeNeedToProductThisTypeOfRes(ResToProduct.Metal, ProductionarHeure.Metal);
       ReasonIDResMax := RESSOURCE_ID_METAL;

       if Result < 0 then begin
          // Impossible de produire du Metal avec cette ProductionParHeure
          exit;
       end;
    end;


    if ResToProduct.Cristal > 0 then begin
       Temp := mbrGetTimeNeedToProductThisTypeOfRes(ResToProduct.Cristal, ProductionarHeure.Cristal);

       if Temp > Result then begin
          Result := Temp;
          ReasonIDResMax := RESSOURCE_ID_CRISTAL;
       end else if Temp < 0 then begin
          // Impossible de produire du Cristal avec cette ProductionParHeure
          Result := Temp;
          exit;
       end;

    end;

    if ResToProduct.Deuterium > 0 then begin
       Temp := mbrGetTimeNeedToProductThisTypeOfRes(ResToProduct.Deuterium, ProductionarHeure.Deuterium);

       if Temp > Result then begin
          Result := Temp;
          ReasonIDResMax := RESSOURCE_ID_DEUTERIUM;
       end else if Temp < 0 then begin
          // Impossible de produire du Deutérium avec cette ProductionParHeure
          Result := Temp;
          exit;
       end;

    end;

end;

procedure UpdateWithTheActivityPropertie(Res: TList);
var i: integer;
    e: POGResUsed;
begin

    if Res.Count = 0 then exit;

    for i := 0 to Res.Count - 1 do begin
       e := Res.Items[i];

       if NoT(e.Activite < 0) then
          SetRessourceActivity(e, e.Activite);
          
    end;

end;

procedure SetRessourceActivity(uRes: POGResUsed; Activite: integer);
begin
    uRes.prMetal     := Trunc((uRes.prMetal * Activite) / 100);
    uRes.prCristal   := Trunc((uRes.prCristal * Activite) / 100);
    uRes.prDeuterium := Trunc((uRes.prDeuterium * Activite) / 100);

    if uRes.prEnergie > 0 then begin
       // Centrale
       uRes.prEnergie :=  Trunc((uRes.prEnergie * Activite) / 100);

    end else begin
       uRes.csEnergieAllow := Trunc((uRes.csEnergieAllow * Activite) / 100);

       // Il faudra bien sur, recalculer le facteur de production
       uRes.csEnergieUsed  := uRes.csEnergieAllow;
    end;


end;

// Renvoi un nombre à virgule compris entre 0 et 1
function GetRessourceWithFactorProduct(Res: TList): double;
var i: integer;
    CurrRes: POGResUsed;

    EnergieProduite : integer;
    EnergieConso    : integer;
begin

    Result := 1.00;


    if Res.Count = 0 then exit;

    for i := 0 to Res.Count - 1 do begin
        CurrRes := Res.Items[i];

        EnergieProduite := EnergieProduite + CurrRes.prEnergie;
        EnergieConso    := EnergieConso    + CurrRes.csEnergieAllow;
    end;

(*
1059 - 1045 = 14

Et l'on aura donc -14 affiché en haut dans les ressources générales
Cependant, on va donc adapter la consomation, et donc obtenir
un facteur de production
Et à la fin, on obtiendra juste -2 dans la consomation d'énergie
dans le tableau de ressource

Le facteur de production est donc:
1045 / 1059 = 0.986

EProduite / EConso = facteur
si supérieur à 1, on le laisse à 1
*)

    if EnergieProduite > EnergieConso then exit;

    if EnergieConso = 0 then begin
       // Aucune énergie consomé, donc le facteur est > 1
       // l'énergie produite est probablement nulle car on n'a pas quitté
       // la fonction au dessus
       exit;
    end;

    Result := EnergieProduite / EnergieConso;

    // Maintenant modifie chaque élément
    for i := 0 to Res.Count - 1 do begin
        CurrRes := Res.Items[i];

        if CurrRes.csEnergieAllow > 0 then begin
           CurrRes.csEnergieUsed := Trunc(CurrRes.csEnergieAllow * Result);

           if CurrRes.prMetal <> 0 then
              CurrRes.prMetal := Trunc((CurrRes.prMetal * Result));

           if CurrRes.prCristal <> 0 then
              CurrRes.prCristal := Trunc((CurrRes.prCristal * Result));

           if CurrRes.prDeuterium <> 0 then
              CurrRes.prDeuterium := Trunc((CurrRes.prDeuterium * Result));
        end;

    end;

end;


procedure GetRessourceWithBestEstimationOfActivity100(Res: TList);
var CurrRes: POGResUsed;
    i: integer;
begin

    if Res.Count = 0 then exit;


    // Effectuer la suppression du facteur de production électrique
    // avant d'appeller cette fonction
    

    for i := 0 to Res.Count - 1 do begin
        CurrRes := res.Items[i];

        if CurrRes.Activite > 0 then begin
           // L'activité doit être au moins de 10% pour pouvoir
           // estimer la valeur à 100%

           if CurrRes.Activite < 100 then begin
// 60% = 2 091
//100% = 3485
               // différent de 0, car il peut être négatif
               if CurrRes.prMetal <> 0 then
                  CurrRes.prMetal     := Trunc((CurrRes.prMetal * 100)  / CurrRes.Activite);

               if CurrRes.prCristal <> 0 then
                  CurrRes.prCristal   := Trunc((CurrRes.prCristal * 100) / CurrRes.Activite);

               if CurrRes.prDeuterium <> 0 then
                  CurrRes.prDeuterium := Trunc((CurrRes.prDeuterium * 100) / CurrRes.Activite);



               if CurrRes.prEnergie > 0 then begin
                   // centrale électrique, même formule
                   CurrRes.prEnergie := Trunc((CurrRes.prEnergie * 100) / CurrRes.Activite);

               end else begin
                   // Sinon seul l'énergie alloué est modifé, pas celle utilisé
                   // Et même formule:
                   CurrRes.csEnergieAllow := Trunc((CurrRes.csEnergieAllow * 100) / CurrRes.Activite);
                   CurrRes.csEnergieUsed  := CurrRes.csEnergieAllow;
               end;

               CurrRes.Activite := 100;
           end;

        end;

    end;

end;

//////////////////////////////////////////////////////////////:
// En paramètre: des pointeurs de POGResUsed, dont la production a été
// bridé par le facteur de production.
// Remet ce facteur à 1, et modifie la puissance consommée
procedure GetRessourceWithoutFactorProduct(Res: TList);
var CurrRes: POGResUsed;
    i: integer;
begin

    if Res.Count = 0 then exit;

    for i := 0 to Res.Count - 1 do begin
        CurrRes := res.Items[i];

        if NoT(CurrRes.Activite < 0) then begin


(* 1114  	  0  	  0  	  371/376
371 = 1114
376 = y
y = (376 * 1114) / 371 = 1129     *)

           if (CurrRes.prEnergie = 0) and (CurrRes.csEnergieUsed > 0) then begin
              // Batiment qui consomme de l'énergie

              if CurrRes.csEnergieAllow <> CurrRes.csEnergieUsed then begin

                 if CurrRes.prMetal <> 0 then
                    CurrRes.prMetal := Trunc((CurrRes.prMetal * CurrRes.csEnergieAllow) / CurrRes.csEnergieUsed);

                 if CurrRes.prCristal <> 0 then
                    CurrRes.prCristal := Trunc((CurrRes.prCristal * CurrRes.csEnergieAllow) / CurrRes.csEnergieUsed);

                 if CurrRes.prDeuterium <> 0 then
                    CurrRes.prDeuterium := Trunc((CurrRes.prDeuterium * CurrRes.csEnergieAllow) / CurrRes.csEnergieUsed);

                 // On repasse au facteur 1
                 CurrRes.csEnergieUsed := CurrRes.csEnergieAllow;

              end;

           end else if CurrRes.prEnergie > 0 then begin
              // Centrale électrique
              // Dans ce cas, la consomation de deut (pour une centrale à fusion)
              // reste la même, même si l'on n'en produit pas assez
              // dans ce cas ça puise dans les réserves que l'on a
              // On a donc aucune modification à apporter à l'élément

           end;
        end;

    end;


end;


function GetBestRatioProduction(ResToProduct: TOGRessource; CurrProduct: TList): integer;
var pE: POGResUsed;
    FullProduct: TOGResUsed;
    TotalEnergie: integer;

    i : integer;
begin
    // La liste de pointeur contient des POGResUsed

    Result := 0;
    if CurrProduct.Count < 3 then exit;

    TotalEnergie := 0;
    
(*
    for i := 0 to CurrProduct.Count - 1 do begin
       pE := CurrProduct.Items[i];

       FullProduct.Production.Metal     := 0;
       FullProduct.Production.Cristal   := 0;
       FullProduct.Production.Deuterium := 0;
       FullProduct.Production.Energie   := 0;
       FullProduct.EnergieFull := 0;
       FullProduct.Activite    := pE.Activite;


       if (pE.Production.Energie > 0) and (pE.EnergieFull = 0) then begin
          // C'est une centrale électrique
          TotalEnergie := TotalEnergie + pE.Production.Energie;

       end else if (pE.EnergieFull > 0) then begin
          // C'est un batiment qui consomme de l'énergie

          if pE.EnergieFull = pE.Production.Energie then begin
             // Ok, c'est déjà au max
             FullProduct.Production.Energie := pE.EnergieFull;
             
          end else begin
             // Ce n'est pas au max
             //   pE.Production.Energie = pE.Production.X
             // FullProduct.EnergieFull = y

             if pE.Production.Metal > 0 then
                FullProduct.Production.Metal     = (pE.Production.Metal * pE.EnergieFull) / pE.Production.Energie;


             if pE.Production.Cristal > 0 then
                FullProduct.Production.Cristal   = (pE.Production.Cristal * pE.EnergieFull) / pE.Production.Energie;

             if pE.Production.Deuterium > 0 then
                FullProduct.Production.Deuterium = (pE.Production.Deuterium * pE.EnergieFull) / pE.Production.Energie;

          end;



       end;

       // Si négatif, alors cela signifie qu'il en consome
       

    end;
*)


end;

function mpcExtractPlanetLocation(s: string): TPlanetLocation;
var xPos: integer;
    u: string;
begin

    // de [1:2:3] ou de 1:2:3
    // On retourne la position
    Result.Galaxie := -1;
    Result.SystemSolaire := -1;
    Result.Position := -1;

    xPos := pos('[', s);
    if xPos > 0 then begin
       System.Delete(s, 1, xPos);
    end;

    xPos := pos(']', s);
    if xPos > 0 then begin
       System.Delete(s, xPos, maxint);
    end;

    // s = '1:2:3'
    xPos := pos(':', s);
    if xPos <= 0 then exit;
    u := copy(s, 1, xPos - 1);
    System.Delete(s, 1, xPos);

    Result.Galaxie := StrToIntDef(u, -1);
    if Result.Galaxie = -1 then exit;



    // s = '2:7'
    xPos := pos(':', s);
    if xPos <= 0 then exit;
    u := copy(s, 1, xPos - 1);
    System.Delete(s, 1, xPos);

    Result.SystemSolaire := StrToIntDef(u, -1);
    if Result.SystemSolaire = -1 then exit;


    // s = '7'
    Result.Position := StrToIntDef(s, -1);
    if Result.Position = -1 then exit;

    

end;


function mpcExtractRessourceFromBuffer(Tampon: string): TOGRessourceAll;
var xPos: integer;
    xStr: string;

    iValue: integer;
    sValue: string;
begin
    // Métal:3.000 Cristal:1.000 Deutérium:0 Énergie:0
    // TODO: On part toujours du principe que le site peut changer, et qu'il
    // y avoir des espaces en plus.

    // On part également du principe que la position de chaque élément
    // peut changer

    Result.Metal     := 0;
    Result.Cristal   := 0;
    Result.Deuterium := 0;
    Result.Energie   := 0;

    Tampon := sfcSupprAccentsAll(lowercase(trim(Tampon)));

    // supprime l'espace eventuel après le deux points pour pouvoir fonctionner dans tous les cas
    Tampon := sfcRemplace(Tampon, ':  ', ':');
    Tampon := sfcRemplace(Tampon, ': ', ':');


    repeat
        xPos := pos(' ', Tampon);

        if xPos = 0 then begin
           xStr := Tampon;
           Tampon := '';
        end else begin
           xStr := copy(Tampon, 1, xPos - 1);
           System.Delete(Tampon, 1, xPos);
           Tampon := Trimleft(Tampon);
        end;

        // Traite le petit tampon xStr
        xPos := pos(':', xStr);

        if xPos = 0 then break;
        sValue := copy(xStr, xPos + 1, maxint);
        sValue := sfcRemplace(sValue, '.', '');

        iValue := StrToIntDef(sValue, -1);

        if iValue < 0 then break;

        case xStr[1] of
            'm': Result.Metal := iValue;
            'c': Result.Cristal := iValue;
            'd': Result.Deuterium := iValue;
            'e': iValue := 0; // non géré... car toujours à 0 dans le jeu 
            else break;
        end;



    until Tampon = '';


end;

function mpcExtractDureeFromStr(DureeStr: string): integer;
const //sfcAbrv = ['h', 'm', 's'];
      sfcIgnore = [' '];
var i, lng: integer;
    sNb, sValue: string;
    iNb: integer;
    bFound: boolean;
begin
    // 8h 18m 47s
    // 1 Jour 1h 36m 00s
    // 9j 3h 25m 42s

    Result := -1;
    DureeStr := trim(DureeStr);
    lng := length(DureeStr);
    if lng < 2 then exit;

    Result := 0;

(*
    i := pos('jour', lowercase(DureeStr));
    if i > 0 then begin
       sNb := copy(DureeStr, 1, i - 1);
       System.Delete(DureeStr, 1, i + 3);
       DureeStr := trimleft(DureeStr);

       try
          iNb := StrToIntDef(sNb, -1);
       except
          iNb := -1;
       end;

       if iNb > 0 then
          Result := Result + iNb * 86400;

    end;
*)


    sNb := '';
    sValue := '';

    for i := 1 to lng do begin
        if DureeStr[i] in sfc_ChiffresSet then begin
           if sValue <> '' then begin
              sValue := '';
           end;
           sNb := sNb + DureeStr[i];

        end else if DureeStr[i] in sfcIgnore then begin
           // on ignore         
           sNb := '';

        end else begin
           // if DureeStr[i] in sfcAbrv then 

           if sNb = '' then begin
              Result := -1;
              exit;
           end;


           iNb := StrToIntDef(sNb, -1);

           if iNb < 0 then begin
              // Erreur
              Result := -1;
              exit;
           end;

           sValue := sValue + DureeStr[i];
           bFound := false;

           if SameText(sValue, LangParsing.GetData('TimeUnit_Day', 'j')) then begin
              Result := Result + iNb * 86400;
              bFound := true;

           end else if SameText(sValue, LangParsing.GetData('TimeUnit_Hour', 'h')) then begin
              Result := Result + iNb * 3600;
              bFound := true;

           end else if SameText(sValue, LangParsing.GetData('TimeUnit_Minute', 'm')) then begin
              Result := Result + iNb * 60;
              bFound := true;

           end else if SameText(sValue, LangParsing.GetData('TimeUnit_Second', 's')) then begin
              Result := Result + iNb ;
              bFound := true;

           end;



           if bFound then begin
              sNb    := '';
              sValue := '';
           end;


        end;

    end;


end;


end.
